package cn.com.bluemoon.daps.standard.service.impl;

import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.com.bluemoon.daps.api.model.RemoteHandlerStdRelateService;
import cn.com.bluemoon.daps.api.sys.RemoteSysDeptService;
import cn.com.bluemoon.daps.common.constant.DapConstant;
import cn.com.bluemoon.daps.common.domain.ResultBean;
import cn.com.bluemoon.daps.common.domain.UserInfoHolder;
import cn.com.bluemoon.daps.common.enums.BmStatus;
import cn.com.bluemoon.daps.common.exception.DapException;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.standard.common.CheckType;
import cn.com.bluemoon.daps.standard.dto.ExcelImportGroup;
import cn.com.bluemoon.daps.standard.entity.DapStandardAuditVersionMain;
import cn.com.bluemoon.daps.standard.entity.DapStandardBasic;
import cn.com.bluemoon.daps.standard.entity.DapStandardBasicDic;
import cn.com.bluemoon.daps.standard.entity.DapSystemCategory;
import cn.com.bluemoon.daps.standard.excel.ExcelImportResultUtils;
import cn.com.bluemoon.daps.standard.excel.easypoi.BasicStandardVerifyHandler;
import cn.com.bluemoon.daps.standard.excel.easypoi.BsAndDicMidVerifyHandler;
import cn.com.bluemoon.daps.standard.excel.easypoi.BsAndDicVerifyHandler;
import cn.com.bluemoon.daps.standard.mapper.DapStandardBasicMapper;
import cn.com.bluemoon.daps.standard.service.DapStandardAuditVersionMainService;
import cn.com.bluemoon.daps.standard.service.DapStandardBasicDicService;
import cn.com.bluemoon.daps.standard.service.DapStandardBasicService;
import cn.com.bluemoon.daps.standard.service.DapSystemCategoryService;
import cn.com.bluemoon.daps.standard.vo.BasicStandardAndDicMiddleVo;
import cn.com.bluemoon.daps.standard.vo.BasicStandardAndDicVo;
import cn.com.bluemoon.daps.system.entity.DapSystemDepartment;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 * 基础标准表 服务实现类
 * </p>
 *
 * @author admin
 * @since 2020-08-03
 */
@Slf4j
@Service
@Transactional(rollbackFor = {DapException.class, Exception.class})
public class DapStandardBasicServiceImpl extends ServiceImpl<DapStandardBasicMapper, DapStandardBasic>
        implements DapStandardBasicService {

    @Resource
    @Lazy
    private DapStandardAuditVersionMainService standardAuditVersionMainService;

    @Resource
    private DapStandardBasicDicService dapStandardBasicDicService;

    @Resource
    private RemoteHandlerStdRelateService dapStandardTBQHandler;

    @Autowired
    private DapSystemCategoryService dapSystemCategoryService;

    @Autowired
    private RemoteSysDeptService dapSystemDepartmentService;

    /**
     * 生成多字段组合唯一键
     *
     * @param keys
     * @return
     */
    public static String genUniqueKey(String... keys) {
        return String.join("#", keys);
    }

    @Override
    public String checkModelOk(DapStandardBasic model, CheckType checkTypes) {
        // 增加针对导入的数据进行跳过，导入已处理
        boolean dataSourceFromExcel = model.getDataSourceFromExcel();
        if (dataSourceFromExcel) {
            log.info("导入数据，跳过系统录入基础标准的校验逻辑");
            return null;
        }
        if (CheckType.UNIQUE.equals(checkTypes)) {
            DapStandardBasic dapStandardBasic = new DapStandardBasic();
            dapStandardBasic.setBmStatus(BmStatus.ON);
            dapStandardBasic.setName(model.getName());
            List<DapStandardBasic> list = list(Wrappers.lambdaQuery(dapStandardBasic)
                    .ne(StringUtils.isNotBlank(model.getStandardUniqueId()), DapStandardBasic::getStandardUniqueId, model.getStandardUniqueId()));
            if (!list.isEmpty()) {
                return "基础标准中已存在中文名称为" + model.getName() + "，不可添加";
            }
            dapStandardBasic.setName(null);
            dapStandardBasic.setEngName(model.getEngName());
            list = list(Wrappers.lambdaQuery(dapStandardBasic).ne(StringUtils.isNotBlank(model.getStandardUniqueId()), DapStandardBasic::getStandardUniqueId, model.getStandardUniqueId()));
            if (!list.isEmpty()) {
                return "基础标准中已存在英文名称为" + model.getEngName() + "，不可添加";
            }
            dapStandardBasic.setName(model.getName());
            list = list(Wrappers.lambdaQuery(dapStandardBasic).ne(StringUtils.isNotBlank(model.getStandardUniqueId()), DapStandardBasic::getStandardUniqueId, model.getStandardUniqueId()));
            if (!list.isEmpty()) {
                return "基础标准中已存在中英文为" + model.getName() + "," + model.getEngName() + "，不可添加 ";
            }
        }
        return null;
    }

    /**
     * 11.基础标准查询
     * 确认后的标准允不允许删除？
     * 允许删除，在被字段已经引用时，进行提示
     * 字段里面的引用关系被级联删除
     * 比如模型表字段中引用的数据id都要清理掉
     * 还要清理标准唯一id对应的审批信息和版本信息 {@link DapStandardAuditVersionMain#getStandardUniqueId()}
     * {@link DapStandardBasic#getStandardUniqueId()}
     *
     * @param standardId
     * @param isLogicDel 是否为逻辑删除 默认为false
     */
    @Override
    public void deleteStandardBasicAndDic(String standardId, boolean isLogicDel) {
        delete(standardId, isLogicDel, true);
    }

    /**
     * 是否逻辑删除，是否多联级删除
     *
     * @param standardId    x
     * @param isLogicDel    是否逻辑删除
     * @param isDelRelation 是否多联级删除
     */
    private void delete(String standardId, boolean isLogicDel, boolean isDelRelation) {
        DapStandardBasic dapStandardBasic = getByIdAsset(standardId, "基础标准不存在，请刷新！");
        // 删除 当前标准和历史版本+审批流程
        String standardUniqueId = dapStandardBasic.getStandardUniqueId();
        // 先删除字典信息 根据uniqueId获取所有历史标准id
        String[] ids = list(bmLambdaQuery().eq(DapStandardBasic::getStandardUniqueId, standardUniqueId).select(DapStandardBasic::getId))
                .stream().map(d -> String.valueOf(d.getId())).toArray(String[]::new);
        dapStandardBasicDicService.deleteByStandardId(ids, isLogicDel);
        // 后删除标准
        if (isLogicDel) {
            removeByIdsAsset(true, standardId);
        } else {
            removeById(standardId);
        }
        if (isDelRelation) {
            standardAuditVersionMainService.assetStandardPublished(standardUniqueId, "标准数据存在未发布版本，请处理后再删除");
//            DapStandardAuditVersionMainMapper standardAuditVersionMainMapper = (DapStandardAuditVersionMainMapper) standardAuditVersionMainService.getBaseMapper();
            if (isLogicDel) {
                // 历史版本+审批流程(去掉删除审批流程）2020年10月13日
                getBaseMapper().deleteLogicByStandardUniqueId(standardUniqueId);
//                standardAuditVersionMainMapper.deleteLogicByStandardUniqueId(standardUniqueId);
            } else {
                // 历史版本+审批流程(去掉删除审批流程）2020年10月13日
                getBaseMapper().deleteByStandardUniqueId(standardUniqueId);
//                standardAuditVersionMainMapper.deleteByStandardUniqueId(standardUniqueId);
            }
            // 其他被引用的接口数据,模型中关联标准数据
            dapStandardTBQHandler.clearRelation(standardUniqueId, DapConstant.DapStandard.StandardType.BASIC);
        }
        log.info("{}删除标准id:{},{}多联级删除 finish", (isLogicDel ? "逻辑" : "物理"), standardId, isDelRelation ? "是" : "不是");
    }

    /**
     * 只删除当前表单相关数据，不对额外的流程，和被关联和被引用的数据
     *
     * @param standardId 标准
     * @param isLogicDel 逻辑
     */
    @Override
    public void deleteSingleStandardBasicAndDic(String standardId, boolean isLogicDel) {
        delete(standardId, isLogicDel, false);
    }

    @Override
    public BasicStandardAndDicVo checkAndThenSaveOrUpdate(BasicStandardAndDicVo basicStandardAndDicVo) {
        String checkModelOk = checkModelOk(basicStandardAndDicVo, CheckType.UNIQUE);
        BmAssetUtils.isTrue(checkModelOk == null, checkModelOk);
        basicStandardAndDicVo.setCreateBy(UserInfoHolder.getAccount());
        boolean save = saveOrUpdate(basicStandardAndDicVo);
        BmAssetUtils.isTrue(save, "写入基础标准数据失败");
        log.info("写入基础数据情况：{}, id:{}", save, basicStandardAndDicVo.getId());
        return basicStandardAndDicVo;
    }

    @Override
    public List<BasicStandardAndDicVo.ModelTableInfo> relateModelTableDataById(String id) {
        DapStandardBasic basic = getById(id);
        // 只要是最新已发布或者已发布过（发布时间不为空）
        BmAssetUtils.isTrue(basic != null && (Boolean.TRUE.equals(basic.getPublishNewest()) || (basic.getPublishTime() != null)), "只允许已发布的标准获取关联模型信息");
        String standardUniqueId = basic.getStandardUniqueId();
        return relateModelTableDataByStandardUniqueId(standardUniqueId);
    }

    @Override
    public List<BasicStandardAndDicVo.ModelTableInfo> relateModelTableDataByStandardUniqueId(String standardUniqueId) {
        List<BasicStandardAndDicVo.ModelTableInfo> modelTableInfos = getBaseMapper().relateModelTableData(standardUniqueId);
        return modelTableInfos;
    }

    /**
     * 解析基础标准导入模板 sheet1
     *
     * @param filePath
     * @return
     */
    @Override
    public ExcelImportResult<BasicStandardAndDicVo> parserSheet1(String filePath) {
        List<Map<String, String>> distinctName = getBaseMapper().findDistinctName();
        Set<String> dbPublishedStandardNames = distinctName.stream().map(m -> m.get("name")).collect(Collectors.toSet());
        Set<String> dbPublishedStandardEngNames = distinctName.stream().map(m -> m.get("eng_name")).collect(Collectors.toSet());
        log.info("初始化数据库中已存在的数据标准，中文名称有：{}个，英文名称：{}个", dbPublishedStandardNames.size(), dbPublishedStandardEngNames.size());
        // 主题分类
        List<DapSystemCategory> systemTopics = dapSystemCategoryService.findTopicsByLevel(0);
        List<DapSystemCategory> systemCategories = dapSystemCategoryService.systemTopicAndCategories("0");
        Map<String, String> departAndIdMap = dapSystemDepartmentService.dbDistinctDepartAndIdMap();
        try (BasicStandardVerifyHandler verifyHandler = new BasicStandardVerifyHandler(dbPublishedStandardNames, dbPublishedStandardEngNames)) {
            verifyHandler.setTopices(systemTopics).setCategories(systemCategories).setDepartment(departAndIdMap);
            ExcelImportResult<BasicStandardAndDicVo> t = ExcelImportResultUtils.excelImportResult(filePath, verifyHandler, BasicStandardAndDicVo.class,
                    0, 1, 0, null);
            t.setMap(verifyHandler.getSheetCacheMap());
            return t;
        }
    }

    @Override
    public ExcelImportResult<BasicStandardAndDicMiddleVo> parserSheet2(String filePath) {
        Map<String, String> distinceBaseCodeAndName = dapStandardBasicDicService.findDistinceBaseCodeAndName();
        try (BsAndDicMidVerifyHandler verifyHandler = new BsAndDicMidVerifyHandler().setDbPublishedStandardCodeAndName(distinceBaseCodeAndName)) {
            ExcelImportResult<BasicStandardAndDicMiddleVo> t = ExcelImportResultUtils.excelImportResult(filePath, verifyHandler, BasicStandardAndDicMiddleVo.class,
                    1, 1, 0, null);
            t.setMap(verifyHandler.getSheetCacheMap());
            return t;
        }
    }

    @Override
    public ExcelImportResult<DapStandardBasicDic> parserSheet3(String filePath) {
        Map<String, String> distinceBaseCodeAndName = dapStandardBasicDicService.findDistinceBaseCodeAndName();
        try (BsAndDicVerifyHandler verifyHandler = new BsAndDicVerifyHandler().setdbStandardDics(distinceBaseCodeAndName)) {
            ExcelImportResult<DapStandardBasicDic> t = ExcelImportResultUtils.excelImportResult(filePath, verifyHandler, DapStandardBasicDic.class,
                    2, 1, 0, new Class[]{ExcelImportGroup.class});
            t.setMap(verifyHandler.getSheetCacheMap());
            return t;
        }
    }

    @SneakyThrows
    @Override
    public void transferDicInfo(DapStandardBasic sourceStandardBasic, BasicStandardAndDicVo targetStandardBasic) {
        CompletableFuture.runAsync(()->{
            if (StringUtils.isNotBlank(sourceStandardBasic.getTopicId())) {
                DapSystemCategory systemCategory = dapSystemCategoryService.getById(sourceStandardBasic.getTopicId());
                Optional.ofNullable(systemCategory).ifPresent(s -> targetStandardBasic.setTopicName(s.getCodeName()));
            }
        }).thenRunAsync(()->{
            if (StringUtils.isNotBlank(sourceStandardBasic.getFirstCategory())) {
                DapSystemCategory systemCategory = dapSystemCategoryService.getById(sourceStandardBasic.getFirstCategory());
                Optional.ofNullable(systemCategory).ifPresent(s -> targetStandardBasic.setFirstCategoryName(s.getCodeName()));
            }
        }).thenRunAsync(()->{
            if (StringUtils.isNotBlank(sourceStandardBasic.getTopicId())) {
                DapSystemCategory systemCategory = dapSystemCategoryService.getById(sourceStandardBasic.getSecondCategory());
                Optional.ofNullable(systemCategory).ifPresent(s -> targetStandardBasic.setSecondCategoryName(s.getCodeName()));
            }
        }).thenRunAsync(()->{
            if (StringUtils.isNotBlank(sourceStandardBasic.getThirdCategory())) {
                DapSystemCategory systemCategory = dapSystemCategoryService.getById(sourceStandardBasic.getThirdCategory());
                Optional.ofNullable(systemCategory).ifPresent(s -> targetStandardBasic.setThirdCategoryName(s.getCodeName()));
            }
        }).thenRunAsync(()->{
            if (StringUtils.isNotBlank(sourceStandardBasic.getDepartmentId())) {
                ResultBean<DapSystemDepartment> department = dapSystemDepartmentService.getById(sourceStandardBasic.getDepartmentId());
                Optional.ofNullable(department.getContent()).ifPresent(s -> targetStandardBasic.setDepartmentName(department.getContent().getDepartName()));
            }
        }).get();
    }


}
